﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using CredentialsManager.Helpers;
using CredentialsManager.Properties;
using MVVMFoundation;

namespace CredentialsManager.ViewModels
{
    public class ChangePasswordViewModel : PasswordViewModel
    {
        #region Private classes

        private class ChangePasswordWorkerInput : SetupWorkerInput
        {
            public bool RequiresQuestionAndAnswer { get; set; }
            public string SecurityAnswer { get; set; }
            public string OldPassword { get; set; }
            public string NewPassword { get; set; }
        }

        #endregion

        #region Private member variables

        private string m_OldPassword;
        private BackgroundWorker m_ChangePasswordWorker;
        private RelayCommand m_ChangeCommand;

        #endregion

        #region Constructor

        public ChangePasswordViewModel(string displayName, string userName,
            string application, PasswordSettings passwordSettings)
            : base(displayName, userName, application, passwordSettings)
        {
            m_ChangePasswordWorker = new BackgroundWorker();
            m_ChangePasswordWorker.Initialise(m_ChangePasswordWorker_DoWork,
                m_ChangePasswordWorker_RunWorkerCompleted, null, false);
        }

        public ChangePasswordViewModel(string userName, string application,
            PasswordSettings passwordSettings)
            : this(Resources.ChangePasswordWindowTitle, userName, application,
                passwordSettings)
        { }

        #endregion

        #region Background worker handlers

        private void m_ChangePasswordWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var input = e.Argument as ChangePasswordWorkerInput;
            Debug.Assert(input != null);

            var proxy = ProxyFactory.Create<PasswordManagerClient, IPasswordManager>(
                ServiceAddressList.CredentialsService);

            try
            {
                if (input.RequiresQuestionAndAnswer)
                {
                    Debug.Assert(!string.IsNullOrEmpty(input.SecurityAnswer));
                    proxy.ChangePasswordWithAnswer(input.Application, input.UserName,
                        input.SecurityAnswer, input.NewPassword);
                }
                else
                {
                    Debug.Assert(!string.IsNullOrEmpty(input.OldPassword));
                    proxy.ChangePasswordWithOldPassword(input.Application, input.UserName,
                        input.OldPassword, input.NewPassword);
                }
            }
            finally
            {
                ProxyFactory.Cleanup<PasswordManagerClient, IPasswordManager>(proxy);
            }
        }

        private void m_ChangePasswordWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                this.IsLoading = false;
                ErrorHandler.HandleNonSpecificWCFException(e.Error, this.DisplayName);
            }
            else
            {
                this.IsLoading = false;

                var messageBox = base.GetService<IMessageBoxService>();
                Debug.Assert(messageBox != null);

                messageBox.Show(Resources.PasswordChangedConfirmationMessage, this.DisplayName,
                    MessageBoxButton.OK, MessageBoxImage.Information);

                this.OnRequestClose();
            }
        }

        #endregion

        #region Public properties

        public string OldPassword
        {
            get
            {
                return m_OldPassword;
            }
            set
            {
                m_OldPassword = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }
        #endregion

        #region Protected methods

        protected override void Validate()
        {
            bool isValid = false;

            Debug.Assert(this.CheckPasswordValidity != null && this.PasswordValidity != null);

            if (this.CheckPasswordValidity.IsValid && this.PasswordValidity.IsValid)
            {
                if (this.RequiresQuestionAndAnswer)
                {
                    isValid = (!string.IsNullOrEmpty(this.SecurityAnswer) &&
                        this.SecurityAnswer.Trim() != string.Empty);
                }
                else
                {
                    isValid = (!string.IsNullOrEmpty(this.OldPassword) &&
                        this.OldPassword.Trim() != string.Empty);
                }
            }

            this.IsValid = isValid;
        }

        #endregion

        #region Private methods

        private void ChangePassword()
        {
            if (!this.IsValid)
            {
                throw new InvalidOperationException(
                    Resources.CannotChangePasswordInvalidOperationException);
            }

            var input = new ChangePasswordWorkerInput()
            {
                RequiresQuestionAndAnswer = this.RequiresQuestionAndAnswer,
                OldPassword = this.OldPassword,
                Application = this.Application,
                UserName = this.UserName,
                SecurityAnswer = this.SecurityAnswer,
                NewPassword = this.Password
            };

            this.IsLoading = true;
            m_ChangePasswordWorker.RunWorkerAsync(input);
        }

        #endregion

        #region Commands

        public ICommand ChangePasswordCommand
        {
            get
            {
                return m_ChangeCommand ?? (m_ChangeCommand = new RelayCommand(
                    param => this.ChangePassword()));
            }
        }

        #endregion
    }
}